﻿using System.Collections.Generic;
using System.IO;
using System.Linq;
using LingDong.HtmlParser;
using LingDong.IndexEntities.Document;
using LingDong.IndexReader;
using NLog;

namespace LingDong.LinkAnalyser
{
    /// <summary>
    /// The commen PageRank is not very effective for the index is too samll
    /// In a 60W page's index, there are only 5K indegrees can be found
    /// SiteRank is similar to PageRank, calculate links from pageA to pageB, 
    /// Where pageA belongs to siteA and pageB belongs to siteB
    /// </summary>
    internal class SiteRank : GGPageRankAlgorithm
    {
        #region Constructors

        public SiteRank(Index index)
        {
            this.index = index;
        }

        #endregion
        

        #region Internal Methods

        internal double[] GetPageSiteRank()
        {
            OutDegree();
            ComputeRank();
            LogSummary(GetSiteNameByID);
            ChangeSiteRankToPageRank();

            return rank;
        }

        #endregion


        #region Private Methods

        private void OutDegree()
        {
            // init site to id hashtable: [site, id]
            int documentCount = index.GetTotalDocumentCount();
            count = 0;
            dicSiteToID = new Dictionary<string, int>();
            dicIDToSite = new Dictionary<int, string>();
            for (int i = 0; i < documentCount; i++)
            {
                OriginalDocumentEnity oriDoc = index.GetOriginalDocument(i);
                string site = HtmlUtility.GetUrlDomain(oriDoc.URL);
                if (!dicSiteToID.ContainsKey(site))
                {
                    dicSiteToID[site] = count;
                    dicIDToSite[count++] = site;
                }
            }

            Init();

            // save doc out links to a temp file
            using (FileStream fs = new FileStream(outLinkTempFile, FileMode.Create))
            {
                for (int i = 0; i < documentCount; i++)
                {
                    OriginalDocumentEnity oriDoc = index.GetOriginalDocument(i);
                    List<string> outLinks = new DocumentWithLinks(oriDoc.Content, oriDoc.URL).References;

                    string curSite = HtmlUtility.GetUrlDomain(oriDoc.URL);
                    int curSiteID = dicSiteToID[curSite];
                    outDegree[curSiteID] = outLinks.Count;

                    List<int> outDocIDList = new List<int>();
                    foreach (string link in outLinks)
                    {
                        string site = HtmlUtility.GetUrlDomain(link);
                        if (dicSiteToID.ContainsKey(site) && site != curSite)
                        {
                            int id = dicSiteToID[site];
                            outDocIDList.Add(id);
                            inDegree[id]++;
                        }
                    }

                    outLinksOffset[curSiteID] = fs.Position;
                    SerializeProvider.Serializer.Serialize(fs, outDocIDList);
                }
            }
        }
        
        private void Init()
        {
            inDegree = new int[count];
            outDegree = new int[count];
            rank = new double[count];

            outLinksOffset = new Dictionary<int, long>();
            outLinkTempFile = Path.GetTempFileName();
        }

        private void ChangeSiteRankToPageRank()
        {
            int documentCount = index.GetTotalDocumentCount();
            double[] pageRank = new double[documentCount];
            for (int i = 0; i < documentCount; i++)
            {
                OriginalDocumentEnity oriDoc = index.GetOriginalDocument(i);
                string site = HtmlUtility.GetUrlDomain(oriDoc.URL);
                if (dicSiteToID.ContainsKey(site))
                {
                    pageRank[i] = rank[dicSiteToID[site]];
                }
            }
            rank = pageRank;
        }

        private string GetSiteNameByID(int id)
        {
            return dicIDToSite[id];
        }

        #endregion
        

        #region Private Members

        private Dictionary<string, int> dicSiteToID;
        private Dictionary<int, string> dicIDToSite;

        #endregion
    }
}
